home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Technology Seed / Mac Tech Seed Feb '96 / Mac Tech Seed Feb '96.toast / pc / develop / include / qtmacros.inc < prev    next >
Encoding:
Text File  |  1996-01-08  |  52.9 KB  |  1,196 lines

  1.  
  2.  
  3. ; ---------------------------------------------------------------------
  4. ;
  5. ; QTMACROS.INC - QuickTime for Windows Helper Macros
  6. ;
  7. ;                Version 1.1
  8. ;
  9. ;                (c) Copyright 1988-1994 Apple Computer, Inc. All Rights Reserved.
  10. ;
  11. ; ---------------------------------------------------------------------
  12.  
  13.  
  14. COMMENT @
  15. Video dispatch codes
  16. @
  17. VDSP_SETBANK     EQU  1
  18. VDSP_SLIDEWINDOW EQU  2
  19. VDSP_SAVECONTEXT EQU  3
  20. VDSP_RESTCONTEXT EQU  4
  21. VDSP_SETTARGET   EQU  5
  22. VDSP_IDENTIFY    EQU 21
  23. VDSP_VERSION     EQU 22
  24. VDSP_BANKTABLE   EQU 23
  25. VDSP_BITBLTTYPE  EQU 24
  26. VDSP_SCANWIDTH   EQU 25
  27. VDSP_LINEAR_BUF  EQU 26
  28. VDSP_TERMINATE   EQU 86
  29.  
  30. COMMENT @
  31. VHDW selectors
  32. @
  33. kSniffVideoHardware     EQU 0
  34. kVideoHardwareSetBounds EQU 1
  35.  
  36. COMMENT @
  37. BitBlt types for hardware support
  38. @
  39. BBL_NONE         EQU  0           ; unknown BitBlt type
  40. BBL_MOVSD        EQU  1           ; use MOVSD
  41. BBL_DRVR         EQU  2           ; use the driver's BitBlt
  42. BBL_MOVPL16      EQU  3           ; 16-bit planar
  43.  
  44. COMMENT @
  45. BMP types
  46. @
  47. BMP_NONE         EQU  0           ; unknown type
  48. BMP_DIB          EQU  1           ; DIB
  49. BMP_MONO         EQU  2           ; monochrome
  50. BMP_PACKED_4     EQU  3           ; packed 4 bit, e.g., Fahrenheit
  51. BMP_PLANAR_4     EQU  4           ; VGA or SVGA
  52. BMP_INDEX_8      EQU  5           ; palettized driver
  53. BMP_5_5_5        EQU  6           ; 32,768 colors
  54. BMP_5_6_5        EQU  7           ; XGA, Intel order
  55. BMP_PLANAR_16    EQU  8           ; two planes of one byte each
  56. BMP_8_8_8_RGB    EQU  9           ; true color RGB
  57. BMP_MEMERR       EQU 10           ; insufficient memory for buffers
  58. BMP_8_8_8_BGR    EQU 11           ; true color BGR
  59. BMP_5_6_5_M      EQU 12           ; XGA, Motorola order
  60. BMP_aRGB32       EQU 13           ; 32-bit with alpha channel
  61. BMP_RGBa32       EQU 14           ; 32-bit with alpha channel
  62. BMP_aBGR32       EQU 15           ; 32-bit with alpha channel
  63. BMP_BGRa32       EQU 16           ; 32-bit with alpha channel
  64.  
  65. ; note below-16:32 selectors are not the base of a
  66. ; selectorArray, therefore huge ptr arithmetic is not valid
  67.  
  68. BMP_IF09         EQU 17            ; YVU9 with difference bits (16:32 selector,linear frame buffer)
  69. BMP_YVU9_PLANAR  EQU 18            ; YVU9 (16:32 selector,linear frame buffer)
  70. BMP_YVU12_PLANAR EQU 19            ; YVU12 (16:32 selector,linear frame buffer)
  71. BMP_YVU9_PACKED  EQU 20            ; YVU9 (16:32 selector,linear frame buffer)
  72. BMP_YVU12_PACKED EQU 21            ; YVU12 (16:32 selector,linear frame buffer)
  73. BMP_YUV_422      EQU 22            ; YUV_422 (16:32 selector,linear frame buffer)
  74. BMP_YUV_411      EQU 23            ; YUV_411 (16:32 selector,linear frame buffer)
  75.  
  76. COMMENT @
  77. Macros AlignBP and AlignBPRet:
  78. Align BP on a DWORD boundary
  79.  
  80. On 386DX and 486 CPUs, applications get better performance by accessing
  81. DWORD operands on DWORD boundaries.  Unaligned operands require an extra
  82. bus cycle.  Our tests on various QuickTime assembler functions shows
  83. unaligned operands typically add a 10% peformance penalty to the entire
  84. function.  By being careful, we can ensure that WORD arguments and WORD
  85. local variables occur in pairs, but we cannot guarantee the alignment of
  86. BP, which is used to access these variables.  The alignment of BP is
  87. determined at run time.  MSC 7.0 aligns the stack only on WORD
  88. boundaries.
  89.  
  90. This macro ensures that BP is aligned on a DWORD boundary.  It is
  91. intended only for use with NEAR PROCs that use the PASCAL calling
  92. sequence.
  93.  
  94. For a NEAR PROC the function arguments and local variables are separated
  95. by two WORDs, the return address and the caller's BP.  Thus, an aligned
  96. BP optimizes accesses to both.  For a FAR PROC the function arguments
  97. and local variables are separated by three WORDs, since the return
  98. address is two WORDs.  In that case, we must define a dummy WORD as the
  99. first local variable and misalign BP, so that the memory references will
  100. be aligned.
  101.  
  102. When BP is not aligned, the macro aligns it, then moves the function
  103. arguments, return address and saved BP accordingly.  The PASCAL
  104. restriction allows the macro to find the byte size of the arguments in
  105. the RET instruction.  The application must insert label "AlignBPRet"
  106. immediately after one of its RET instructions.  The application must
  107. also define a dummy WORD as the last local variable.
  108.  
  109. The macro assumes the direction indicator is 0 (forward), that the
  110. application already saved SI and DI (via USES SI DI on the PROC
  111. statement), and that ES and CX can be destroyed.
  112.  
  113. Upon exit from the function, we must adjust the stack pointer if BP was
  114. changed at entry.  We accomplish this by storing in the extra word made
  115. available at the top of the stack the caller's return address.  At the
  116. location in the stack that normally holds the caller's return address,
  117. we place an address in this program.  At the doctored return address, we
  118. execute a near return.
  119.  
  120. Macro AlignBPExit uses a machine instruction for the near RET to prevent
  121. the assembler from generating epilog code.
  122. @
  123. ifndef _QTW32_
  124. AlignBP  MACRO
  125.          LOCAL   AlreadyAligned
  126.          TEST    BP, 2            ;; Already on DWORD boundary?
  127.          JE      AlreadyAligned   ;; Skip if already aligned
  128.          CMP     BYTE PTR AlignBPRet - 3, 0C2H ;; Expected RET instruction?
  129.          JNE     AlreadyAligned   ;; Don't adjust if environment unknown
  130.          MOV     CX, WORD PTR [AlignBPRet - 2];; Get byte size of parameter list
  131.          SHR     CX, 1            ;; Convert byte count to word count
  132.          ADD     CX, 2            ;; Account for return address and saved BP
  133.          SUB     BP, 2            ;; Perform the alignment ourselves
  134.                                   ;; Requires a word of slop after all local
  135.                                   ;; variables
  136.          PUSH    SS               ;; Copy SS to ES
  137.          POP     ES               ;;
  138.          MOV     DI, BP           ;; Set target pointer
  139.          LEA     SI, [DI+2]       ;; Set source pointer
  140.          CLD
  141.          REP     MOVSW [DI],SS:[SI] ;; Move the arguments down
  142.          MOV     AX, [BP+2]       ;; Get caller's return address
  143.          MOV     ES:[DI], AX      ;; Store at top of stack
  144.          MOV     [BP+2], OFFSET AlignFixSP ;; Substitute our return address
  145. AlreadyAligned:
  146.          ENDM
  147. else
  148. AlignBP  MACRO
  149.          ENDM
  150. endif
  151.  
  152. ifndef _QTW32_
  153. AlignFarBP  MACRO
  154.          LOCAL   AlreadyAligned
  155.          TEST    BP, 2            ;; Already on DWORD boundary?
  156.          JE      AlreadyAligned   ;; Skip if already aligned
  157.          CMP     BYTE PTR AlignFarBPRet - 3, 0CAH ;; Expected RET instruction?
  158.          JNE     AlreadyAligned   ;; Don't adjust if environment unknown
  159.          MOV     CX, WORD PTR [AlignFarBPRet - 2];; Get byte size of parameter list
  160.          SHR     CX, 1            ;; Convert byte count to word count
  161.          ADD     CX, 3            ;; Account for return address:selector and saved BP
  162.          SUB     BP, 6            ;; Perform the alignment ourselves
  163.                                   ;; Requires 6 bytes of slop after all local
  164.                                   ;; variables
  165.          PUSH    SS               ;; Copy SS to ES
  166.          POP     ES               ;;
  167.          MOV     DI, BP           ;; Set target pointer
  168.          LEA     SI, [DI+6]       ;; Set source pointer
  169.          CLD
  170.          REP     MOVSW [DI],SS:[SI] ;; Move the arguments down
  171.          MOV     AX, [BP+2]       ;; Get caller's return address offset
  172.          MOV     ES:[DI], AX      ;; Store at top of stack
  173.          mov     ax,[bp+4]        ;; get caller's return address selector
  174.          mov     es:[di+2], ax    ;; store caller's code selector
  175.          MOV     [BP+2], OFFSET AlignFixSP ;; Substitute our return address
  176. AlreadyAligned:
  177.          ENDM
  178. else
  179. AlignFarBP MACRO
  180.          ENDM
  181. endif
  182.  
  183. AlignBPExit MACRO
  184. ifndef _QTW32_
  185. AlignBPRet:                       ;; Immediately follow the RETN immed
  186.          ALIGN   16               ;; Align for better performance
  187. AlignFixSP:
  188.          RETN                     ;; Return to caller without epilog
  189. endif
  190.          ENDM
  191.  
  192. AlignFarBPExit MACRO
  193. ifndef _QTW32_
  194. AlignFarBPRet:                       ;; Immediately follow the RETF immed
  195.          ALIGN   16               ;; Align for better performance
  196. AlignFixSP:
  197.          RETF                     ;; Return to caller without epilog
  198. endif
  199.          ENDM
  200.  
  201. COMMENT @
  202. Macro BSWAPX:
  203. Exchange the bytes in a DWORD register.
  204.  
  205. On a 486, the BSWAP instruction uses 1 clock instead of the 6 or 7
  206. clocks required for this macro. Unfortunately, there is no cheap
  207. inline way at run time to distinguish between a 386 and 486.
  208. @
  209. BSWAPX   MACRO   reg:REQ
  210. Root     SUBSTR  <reg>, 2, 1
  211. Xchg8L    CATSTR  Root, <L>
  212. Xchg8H    CATSTR  Root, <H>
  213.          xchg    Xchg8L,Xchg8H
  214.          ROL     reg, 16
  215.          xchg    Xchg8L,Xchg8H
  216.          ENDM
  217.  
  218. COMMENT @
  219. Macro to initialize dirty list, initialize bank bounds table, set GS:0
  220. to bank bounds table, set ES:EDI to initial hardware target address,
  221. set the bank for banked adapters.
  222. @
  223. ifndef _QTW32_
  224. SetHdwTarget MACRO
  225.          LGS     DI, BankBds      ;; Point to bank bounds table
  226.          MOV     DI, GS:[DI]      ;; Get residual block count of first bad row
  227.          MOV     NextBadBlock, DI ;; Save the residual block count to match
  228.  
  229.          MOV     AX, WORD PTR OffScreen ;; Point to dirty list
  230.          REPEAT  BPPT * Mag
  231.          ADD     AX, BWIDTH       ;; Allocate room for offscreen pixels
  232.          ENDM                     ;; of REPEAT  BPPT * Mag
  233.          IF      Mag EQ 2
  234.          ADD     AX, WdBytes      ;; Allocate room for second scan line in pair
  235.          ENDIF
  236.          INC     AX               ;; Round up to even offset for efficiency
  237.          AND     AL, 0FEH         ;;
  238.          ADD     AX, 4            ;; Leave room for post-processing
  239.          MOV     DirtyListFirst, AX ;;
  240.          MOV     AX, DESTY        ;; Get starting row
  241.          MUL     WdBytes          ;; Multiply by width of adapter scan line
  242.          REPEAT  BPPT             ;; Bytes per target pixel
  243.          ADD     AX, DESTX        ;; Add in destination column
  244.          ADC     DX, 0            ;; Bump bank number if carry
  245.          ENDM                     ;; of REPEAT BPPT
  246.          PUSH    DX               ;; Push high order word
  247.          PUSH    AX               ;; Push low order word
  248.          PUSH    VDSP_SETTARGET   ;; Dispatch code for SetTarget
  249.          CALL    [Hardware]       ;; Set the initial bank, also ES:DI
  250.          ADD     SP, 6            ;; Remove arguments from the stack
  251.          MOV     Bank, AX         ;; Save the returned bank
  252.          MOV     SelVRAM, ES      ;; Save VRAM selector
  253.          ENDM                     ;; of SetHdwTarget MACRO
  254. else
  255. SetHdwTarget MACRO
  256.          MOV     EDI, BankBds     ;; Point to bank bounds table
  257.          MOVZX   EDI, WORD PTR [EDI] ;; Get residual block count of first bad row
  258.          MOV     NextBadBlock, EDI;; Save the residual block count to match
  259.  
  260.          MOV     EAX, DWORD PTR OffScreen ;; Point to dirty list
  261.          MOVZX   EDX, BWIDTH
  262.          REPEAT  BPPT * Mag
  263.          ADD     EAX, EDX         ;; Allocate room for offscreen pixels
  264.          ENDM                     ;; of REPEAT  BPPT * Mag
  265.          IF      Mag EQ 2
  266.          ADD     EAX, WdBytes     ;; Allocate room for second scan line in pair
  267.          ENDIF
  268.          INC     EAX              ;; Round up to even offset for efficiency
  269.          AND     AL, 0FEH         ;;
  270.          ADD     EAX, 4           ;; Leave room for post-processing
  271.          MOV     DirtyListFirst, EAX ;;
  272.          MOVZX   EAX, DESTY        ;; Get starting row
  273.          MUL     WdBytes           ;; Multiply by width of adapter scan line
  274.          MOVZX   EDX, DESTX
  275.          REPEAT  BPPT             ;; Bytes per target pixel
  276.          ADD     EAX, EDX        ;; Add in destination column
  277.          ENDM                     ;; of REPEAT BPPT
  278.          PUSH    EAX               ;; Push target address
  279.          PUSH    VDSP_SETTARGET   ;; Dispatch code for SetTarget
  280.          CALL    [Hardware]       ;; Set the initial bank, also EDI
  281.          ADD     ESP, 8            ;; Remove arguments from the stack
  282.          MOV     Bank, EAX
  283.          ENDM                     ;; of SetHdwTarget MACRO
  284. endif
  285.  
  286. COMMENT @
  287. Bank change macros:
  288. Used by functions that write directly to video adapter hardware.
  289.  
  290. The bank change macros assume that all bank changes are of size 1.  This
  291. is so, since the program moves at most 4 rows at a time and each bank holds
  292. at least 40 rows.
  293. @
  294. DoBankUp MACRO
  295.          INC     Bank             ;; Go to next bank
  296.          PUSH    Bank             ;; Pass argument to function
  297.          PUSH    VDSP_SETBANK     ;; Dispatch code for SetBank
  298.          CALL    [Hardware]       ;; Set the new bank
  299. ifndef _QTW32_
  300.          ADD     SP, 4            ;; Remove arguments from the stack
  301. else
  302.          ADD     ESP, 8           ;; Remove arguments from the stack
  303. endif
  304.          ENDM
  305.  
  306. CheckBankUp MACRO  nbr:REQ        ;; Check bank, jumping only when the bank
  307.          JC      NewBankUp&nbr&   ;;  changes
  308. NewBankUp&nbr&Ret:
  309.          ENDM
  310.  
  311. SetBankUp MACRO  nbr:REQ          ;; Effect a bank change
  312.          ALIGN   16
  313. NewBankUp&nbr&:
  314.          DoBankUp
  315.          JMP     NewBankUp&nbr&Ret;; Return to main code
  316.          ENDM
  317.  
  318. DoBankDn MACRO
  319.          DEC     Bank             ;; Go to previous bank
  320.          PUSH    Bank             ;; Pass argument to function
  321.          PUSH    VDSP_SETBANK     ;; Dispatch code for SetBank
  322.          CALL    [Hardware]       ;; Set the new bank
  323. ifndef _QTW32_
  324.          ADD     SP, 4            ;; Remove arguments from the stack
  325. else
  326.          ADD     ESP, 8           ;; Remove arguments from the stack
  327. endif
  328.          ENDM
  329.  
  330. CheckBankDn MACRO  nbr:REQ        ;; Check bank, jumping only when the bank
  331.          JC      NewBankDn&nbr&   ;;  changes
  332. NewBankDn&nbr&Ret:
  333.          ENDM
  334.  
  335. SetBankDn MACRO  nbr:REQ          ;; Effect a bank change
  336.          ALIGN   16
  337. NewBankDn&nbr&:
  338.          DoBankDn
  339.          JMP     NewBankDn&nbr&Ret;; Return to main code
  340.          ENDM
  341.  
  342. SlideWindow MACRO
  343.          PUSH    Bank             ;; SlideWindow may adjust this
  344.          PUSH    VDSP_SLIDEWINDOW ;; Dispatch code for SlideWindow
  345.          CALL    [Hardware]       ;; Call the function
  346. ifndef _QTW32_
  347.          ADD     SP, 4            ;; Remove arguments from the stack
  348.                                   ;; AX = bank, ES:DI may be changed
  349.          MOV     Bank, AX         ;; Bank number may have changed
  350. else
  351.          ADD     ESP, 8           ;; Remove arguments from the stack
  352.                                   ;; AX = bank, ES:DI may be changed
  353.          MOV     Bank, EAX        ;; Bank number may have changed
  354. endif
  355.          ENDM
  356.  
  357. COMMENT @
  358. Macros used with downward dithers from 24 bit sources
  359.  
  360. Get554 is used with 8 bit targets.  If the 2nd parameter (tableBase) is
  361. not present, then the resulting 5/5/4 pixel is placed in EBX, where it
  362. can then be used as an index into a dither table.  If tableBase *is*
  363. present, the resulting 5/5/4 pixel is placed in EBX, and tableBase is
  364. added to it.  This is particularly useful in flat-model 32-bit code,
  365. where the base of the table is not in a segment register.
  366.  
  367. Get555 is used with 15 bit (5-5-5) targets.
  368.  
  369. Get565 and Get565M are used with 16 bit (5-6-5) targets.
  370. @
  371. Get554   MACRO   offst:REQ, tableBase             ;;
  372.          XOR     EBX, EBX              ;; some 16-bit code uses EBX as index
  373.          MOV     BH, [ESI+offst]       ;; Get R
  374.          SHR     BH, 3                 ;; Save 5 bits of R
  375.          MOV     BL, [ESI+offst+1]     ;; Get G
  376.          SHL     EBX, 5                ;; Save 5 bits of G
  377.          MOV     BL, [ESI+offst+2]     ;; Get B
  378.          SHR     EBX, 4                ;; Get 5-5-4
  379.          IFNB <tableBase>
  380.              ADD     EBX, tableBase
  381.          ENDIF
  382.          ENDM
  383.  
  384. Get555   MACRO   offst:REQ
  385.          MOV     AH, [ESI+offst]       ;; Get R
  386.          SHR     AH, 3                 ;; Save 5 bits of R
  387.          MOV     AL, [ESI+offst+1]     ;; Get G
  388.          SHL     EAX, 5                ;; Save 5 bits of G
  389.          MOV     AL, [ESI+offst+2]     ;; Get B
  390.          SHR     EAX, 3                ;; Get 5-5-5
  391.          ENDM
  392.  
  393. Get565   MACRO   offst:REQ
  394.          MOV     AH, [ESI+offst]       ;; Get R
  395.          SHR     AH, 3                 ;; Save 5 bits of R
  396.          MOV     AL, [ESI+offst+1]     ;; Get G
  397.          SHL     EAX, 6                ;; Save 6 bits of G
  398.          MOV     AL, [ESI+offst+2]     ;; Get B
  399.          SHR     EAX, 3                ;; Get 5-6-5
  400.          ENDM
  401.  
  402. Get565M  MACRO   offst:REQ
  403.          Get565  offst                 ;; Get 5-6-5 in Intel order
  404.          XCHG    AH,AL                 ;; Put bytes in Motorola order
  405.          ENDM
  406.  
  407. COMMENT @
  408. Utility macros
  409.  
  410. The macro assumes CX bytes will be moved from DS:SI to ES:DI, and that
  411. DX is available.
  412. @
  413. @Equals  MACRO   p1:REQ, p2:REQ
  414.          EXITM   %( @InStr(,p1,p2) * @InStr(,p2,p1))
  415.          ENDM                     ;; of @Equals MACRO
  416.  
  417. CopyBytes MACRO
  418.          CLD
  419.          MOV     DX, CX           ;; Save byte count
  420.          SHR     CX, 2            ;; Byte count to DWORD count
  421.          REP     MOVSD            ;; Copy most of the bytes
  422.          MOV     CX, DX           ;; Restore byte count
  423.          AND     CX, 3            ;; 0-3 bytes left
  424.          SHR     CX, 1            ;; Check for 2 or more bytes left
  425.          REP     MOVSW            ;; Copy stray WORD, if any
  426.          ADC     CX, CX           ;; 0-1 bytes left
  427.          REP     MOVSB            ;; Copy stray byte, if any
  428.          ENDM                     ;; of CopyBytes MACRO
  429.  
  430. COMMENT @
  431. Macros used with decompressors that write directly to video adapter
  432. hardware.
  433. @
  434. GoOnScreenX MACRO
  435.          ALIGN   16
  436. GoOnScreen:
  437. ifndef _QTW32_
  438.          PUSH    CX               ;; Save register
  439.          MOV     CX, DirtyListNext     ;; Offset of next dirty list entry
  440.          SUB     CX, DirtyListFirst    ;; 4 * number of dirty list entries
  441.          JLE     NotDirty         ;; Skip if dirty list is empty
  442.  
  443.          PUSH    BX               ;; Save registers
  444.          PUSH    DX               ;;
  445.          PUSH    SI               ;;
  446.          PUSH    DI               ;;
  447.          PUSH    DS               ;;
  448.          PUSH    ES               ;;
  449.  
  450.          SHR     CX, 2            ;; Number of dirty list entries
  451.          MOV     DX, BadStart     ;; Onscreen offset of line start
  452.          NEG     DX               ;; Offset in offscreen buffer of bank split
  453.                                   ;; Merge adjacent ranges
  454.                                   ;; Split a range that splits a bank
  455.          MOV     DS, WORD PTR OffScreen +2  ;; Offscreen buffer is the source
  456.          MOV     SI, DirtyListFirst         ;; Point to first dirty list entry
  457.          LEA     DI, [SI-4]                 ;; Allow room for one split entry
  458. LoopNewRange:
  459.          MOV     AX, [SI]         ;; Get start of range
  460.          MOV     [DI], AX         ;; Save start of range
  461. LoopSameRange:
  462.          MOV     BX, [SI+2]       ;; Get end of range
  463.          ADD     SI, 4            ;; Point to next source slot
  464.          DEC     CX               ;; One fewer source slot
  465.          JLE     RangeDone        ;; Skip if no more source slots
  466.          CMP     BX, [SI]         ;; Is the next range adjacent to this?
  467.          JB      RangeDone        ;; Skip if ranges are not adjacent
  468.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  469. RangeDone:
  470.          CMP     AX, DX           ;; Might split occur in this range?
  471.          JGE     NoSplit          ;; Skip if split does not occur in this range
  472.          CMP     BX, DX           ;; Does split occur in this range?
  473.          JLE     NoSplit          ;; Skip if split does not occur in this range
  474.          MOV     [DI+2], DX       ;;
  475.          ADD     DI, 4            ;; Point to next target slot
  476.          MOV     [DI], DX         ;; Start of new range
  477. NoSplit:
  478.          MOV     [DI+2], BX       ;; Set right edge of range
  479.          ADD     DI, 4            ;; Point to next target slot
  480.          TEST    CX, CX           ;; Any source slots left?
  481.          JG      LoopNewRange     ;; Loop while source slots remain
  482.          OR      WORD PTR [DI], -1;; Terminate the list
  483.                                   ;; Make offsets relative to prior entries
  484.                                   ;; Convert ending offsets to byte sizes
  485.          LEA     CX, [DI+4]       ;; Compute number of list entries
  486.          SUB     CX, DirtyListFirst    ;; 4 * number of WORDs in the list
  487.          LEA     BX, [DI-2]       ;; Point to last WORD in list
  488.          SHR     CX, 1            ;; 2 * number of WORDs in list
  489.          DEC     CX               ;; Don't process first WORD
  490. LoopDiff:
  491.          MOV     AX, [BX-2]       ;; Get previous WORD in list
  492.          SUB     [BX], AX         ;; Compute difference from previous WORD
  493.          SUB     BX, 2            ;; Back up one WORD
  494.          DEC     CX               ;; One fewer WORD
  495.          JG      LoopDiff         ;; Loop once for each WORD in list
  496.                                   ;; Copy the first line of the pair
  497.          MOV     ES, SelVRAM      ;; VRAM is the target
  498.          MOV     DI, BadStart     ;;
  499.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  500. LoopCopy:
  501.          MOV     AX, [BX]         ;; Get offset of range start
  502.          TEST    AX, AX           ;; End of the list?
  503.          JL      DirtyDone        ;; Skip if end of the list
  504.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  505.          ADD     BX, 4            ;; Point to next list entry
  506.          ADD     SI, AX           ;; Locate data in offscreen buffer
  507.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  508.          CheckBankUp 01E          ;; Change bank if necessary
  509.          CopyBytes                ;; Copy the bytes
  510.          TEST    DI, DI           ;; Did range end on a bank boundary?
  511.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  512. NewBankUp02ERet:
  513.          JMP     LoopCopy         ;; Loop once for each range
  514.  
  515.          ALIGN   16
  516. DirtyDone:
  517.          TEST    DI, DI           ;; Did we cross the bank boundary?
  518.          JGE     DirtyCrossed     ;; Skip if we already crossed
  519.          DoBankUp                 ;; Cross the bank boundary
  520. DirtyCrossed:
  521.          POP     ES               ;; Restore registers
  522.          POP     DS               ;;
  523.          POP     DI               ;;
  524.          POP     SI               ;;
  525.          POP     DX               ;;
  526.          POP     BX               ;;
  527.          POP     CX               ;;
  528.          RETN                     ;; Return to caller without epilog
  529.  
  530.          ALIGN   16
  531. NotDirty:
  532.          POP     CX               ;; Restore register
  533.          DoBankUp                 ;; Go to next bank
  534.          RETN                     ;; Return to caller without epilog
  535. else
  536.          PUSH    ECX              ;; Save register
  537.          MOV     ECX, DirtyListNext    ;; Offset of next dirty list entry
  538.          SUB     ECX, DirtyListFirst   ;; 4 * number of dirty list entries
  539.          JLE     NotDirty         ;; Skip if dirty list is empty
  540.  
  541.          PUSH    EBX              ;; Save registers
  542.          PUSH    EDX              ;;
  543.          PUSH    ESI              ;;
  544.          PUSH    EDI              ;;
  545.          XOR     EAX, EAX
  546.          XOR     EBX, EBX
  547.  
  548.          SHR     ECX, 2           ;; Number of dirty list entries
  549.          MOV     EDX, BadStart    ;; Onscreen offset of line start
  550.          NEG     EDX              ;; Offset in offscreen buffer of bank split
  551.                                   ;; Merge adjacent ranges
  552.                                   ;; Split a range that splits a bank
  553. ;         MOV     DS, WORD PTR OffScreen +2 ;; Offscreen buffer is the source
  554.          MOV     ESI, DirtyListFirst        ;; Point to first dirty list entry
  555.          LEA     EDI, [ESI-4]               ;; Allow room for one split entry
  556. LoopNewRange:
  557.          MOV     AX, [ESI]        ;; Get start of range
  558.          MOV     [EDI], AX        ;; Save start of range
  559. LoopSameRange:
  560.          MOV     BX, [ESI+2]      ;; Get end of range
  561.          ADD     ESI, 4           ;; Point to next source slot
  562.          DEC     CX               ;; One fewer source slot
  563.          JLE     RangeDone        ;; Skip if no more source slots
  564.          CMP     BX, [ESI]        ;; Is the next range adjacent to this?
  565.          JB      RangeDone        ;; Skip if ranges are not adjacent
  566.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  567. RangeDone:
  568.          CMP     AX, DX           ;; Might split occur in this range?
  569.          JGE     NoSplit          ;; Skip if split does not occur in this range
  570.          CMP     BX, DX           ;; Does split occur in this range?
  571.          JLE     NoSplit          ;; Skip if split does not occur in this range
  572.          MOV     [EDI+2], DX      ;;
  573.          ADD     EDI, 4           ;; Point to next target slot
  574.          MOV     [EDI], DX        ;; Start of new range
  575. NoSplit:
  576.          MOV     [EDI+2], BX      ;; Set right edge of range
  577.          ADD     EDI, 4           ;; Point to next target slot
  578.          TEST    CX, CX           ;; Any source slots left?
  579.          JG      LoopNewRange     ;; Loop while source slots remain
  580.          OR      WORD PTR [EDI], -1;; Terminate the list
  581.                                   ;; Make offsets relative to prior entries
  582.                                   ;; Convert ending offsets to byte sizes
  583.          LEA     ECX, [EDI+4]     ;; Compute number of list entries
  584.          SUB     ECX, DirtyListFirst   ;; 4 * number of WORDs in the list
  585.          LEA     EBX, [EDI-2]     ;; Point to last WORD in list
  586.          SHR     ECX, 1           ;; 2 * number of WORDs in list
  587.          DEC     ECX              ;; Don't process first WORD
  588. LoopDiff:
  589.          MOV     AX, [EBX-2]      ;; Get previous WORD in list
  590.          SUB     [EBX], AX        ;; Compute difference from previous WORD
  591.          SUB     EBX, 2           ;; Back up one WORD
  592.          DEC     CX               ;; One fewer WORD
  593.          JG      LoopDiff         ;; Loop once for each WORD in list
  594.                                   ;; Copy the first line of the pair
  595. ;         MOV     ES, SelVRAM     ;; VRAM is the target
  596.          MOV     EDI, BadStart    ;;
  597.          MOV     ESI, DWORD PTR OffScreen     ;; Offscreen buffer is the source
  598. LoopCopy:
  599.          MOV     AX, [EBX]        ;; Get offset of range start
  600.          TEST    AX, AX           ;; End of the list?
  601.          JL      DirtyDone        ;; Skip if end of the list
  602.          MOV     CX, [EBX+2]      ;; Get number of bytes in the range
  603.          ADD     EBX, 4           ;; Point to next list entry
  604.          MOVZX   EAX, AX
  605.          ADD     ESI, EAX         ;; Locate data in offscreen buffer
  606.          ADD     EDI, EAX         ;; Position pointer in onscreen buffer
  607.          CheckBankUp 01E          ;; Change bank if necessary
  608.          CopyBytes                ;; Copy the bytes
  609.          TEST    EDI, EDI         ;; Did range end on a bank boundary?
  610.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  611. NewBankUp02ERet:
  612.          JMP     LoopCopy         ;; Loop once for each range
  613.  
  614.          ALIGN   16
  615. DirtyDone:
  616.          TEST    EDI, EDI         ;; Did we cross the bank boundary?
  617.          JGE     DirtyCrossed     ;; Skip if we already crossed
  618.          DoBankUp                 ;; Cross the bank boundary
  619. DirtyCrossed:
  620.          POP     EDI              ;;
  621.          POP     ESI              ;;
  622.          POP     EDX              ;;
  623.          POP     EBX              ;;
  624.          POP     ECX              ;;
  625.          RETN                     ;; Return to caller without epilog
  626.  
  627.          ALIGN   16
  628. NotDirty:
  629.          POP     ECX              ;; Restore register
  630.          DoBankUp                 ;; Go to next bank
  631.          RETN                     ;; Return to caller without epilog
  632. endif
  633.          ENDM                     ;; of GoOnScreenX
  634.  
  635. GoOnScreen2x MACRO
  636.          ALIGN   16
  637. GoOnScreen:
  638. ifndef _QTW32_
  639.          PUSH    CX               ;; Save register
  640.          MOV     CX, DirtyListNext     ;; Offset of next dirty list entry
  641.          SUB     CX, DirtyListFirst    ;; 4 * number of dirty list entries
  642.          JLE     NotDirty         ;; Skip if dirty list is empty
  643.  
  644.          PUSH    BX               ;; Save registers
  645.          PUSH    DX               ;;
  646.          PUSH    SI               ;;
  647.          PUSH    DI               ;;
  648.          PUSH    DS               ;;
  649.          PUSH    ES               ;;
  650.  
  651.          SHR     CX, 2            ;; Number of dirty list entries
  652.          MOV     BX, BadStart     ;; Onscreen offset of line start
  653.          MOV     DX, BX           ;; Copy onscreen offset of line start
  654.          NEG     DX               ;; Offset in offscreen buffer of bank split?
  655.          ADD     BX, WdBytes      ;; Onscreen offset of second line in pair
  656.          JC      FirstLineSplit   ;; Skip if first line in pair has bank split
  657.          MOV     DX, BX           ;; Copy onscreen offset of line start
  658.          NEG     DX               ;; Offset in offscreen buffer of bank split
  659. FirstLineSplit:
  660.                                   ;; Merge adjacent ranges
  661.                                   ;; Split a range that splits a bank
  662.          MOV     DS, WORD PTR OffScreen +2  ;; Offscreen buffer is the source
  663.          MOV     SI, DirtyListFirst         ;; Point to first dirty list entry
  664.          LEA     DI, [SI-4]                 ;; Allow room for one split entry
  665. LoopNewRange:
  666.          MOV     AX, [SI]         ;; Get start of range
  667.          MOV     [DI], AX         ;; Save start of range
  668. LoopSameRange:
  669.          MOV     BX, [SI+2]       ;; Get end of range
  670.          ADD     SI, 4            ;; Point to next source slot
  671.          DEC     CX               ;; One fewer source slot
  672.          JLE     RangeDone        ;; Skip if no more source slots
  673.          CMP     BX, [SI]         ;; Is the next range adjacent to this?
  674.          JB      RangeDone        ;; Skip if ranges are not adjacent
  675.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  676. RangeDone:
  677.          CMP     AX, DX           ;; Might split occur in this range?
  678.          JGE     NoSplit          ;; Skip if split does not occur in this range
  679.          CMP     BX, DX           ;; Does split occur in this range?
  680.          JLE     NoSplit          ;; Skip if split does not occur in this range
  681.          MOV     [DI+2], DX       ;;
  682.          ADD     DI, 4            ;; Point to next target slot
  683.          MOV     [DI], DX         ;; Start of new range
  684. NoSplit:
  685.          MOV     [DI+2], BX       ;; Set right edge of range
  686.          ADD     DI, 4            ;; Point to next target slot
  687.          TEST    CX, CX           ;; Any source slots left?
  688.          JG      LoopNewRange     ;; Loop while source slots remain
  689.                                   ;;
  690.          OR      WORD PTR [DI], -1;; Terminate the list
  691.                                   ;; Make offsets relative to prior entries
  692.                                   ;; Convert ending offsets to byte sizes
  693.          LEA     CX, [DI+4]       ;; Compute number of list entries
  694.          SUB     CX, DirtyListFirst    ;; 4 * number of WORDs in the list
  695.          LEA     BX, [DI-2]       ;; Point to last WORD in list
  696.          SHR     CX, 1            ;; 2 * number of WORDs in list
  697.          DEC     CX               ;; Don't process first WORD
  698. LoopDiff:
  699.          MOV     AX, [BX-2]       ;; Get previous WORD in list
  700.          SUB     [BX], AX         ;; Compute difference from previous WORD
  701.          SUB     BX, 2            ;; Back up one WORD
  702.          DEC     CX               ;; One fewer WORD
  703.          JG      LoopDiff         ;; Loop once for each WORD in list
  704.  
  705.                                   ;; Copy the first line of the pair
  706.          MOV     ES, SelVRAM      ;; VRAM is the target
  707.          MOV     DI, BadStart     ;;
  708.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  709. LoopCopy1:
  710.          MOV     AX, [BX]         ;; Get offset of range start
  711.          TEST    AX, AX           ;; End of the list?
  712.          JL      DirtyDone1       ;; Skip if end of the list
  713.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  714.          ADD     BX, 4            ;; Point to next list entry
  715.          ADD     SI, AX           ;; Locate data in offscreen buffer
  716.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  717.          CheckBankUp 01E          ;; Change bank if necessary
  718.          CopyBytes                ;; Copy the bytes
  719.          TEST    DI, DI           ;; Did range end on a bank boundary?
  720.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  721. NewBankUp02ERet:
  722.          JMP     LoopCopy1        ;; Loop once for each range
  723.  
  724.          ALIGN   16
  725.                                   ;; Copy the second line of the pair
  726. DirtyDone1:
  727.          MOV     AX, BadStart     ;; Onscreen offset of first line in pair
  728.          ADD     AX, WdBytes      ;; Onscreen offset of second line in pair
  729.          SUB     AX, DI           ;; Offset from current target pointer
  730.          ADD     DI, AX           ;; Add the offset back in
  731.          CheckBankUp 03E          ;; Change bank if necessary
  732.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  733.          MOV     BX, DirtyListFirst         ;; Point to first dirty list entry
  734.          SUB     BX, 4                      ;; Post-processed first entry
  735. LoopCopy2:
  736.          MOV     AX, [BX]         ;; Get offset of range start
  737.          TEST    AX, AX           ;; End of the list?
  738.          JL      DirtyDone2       ;; Skip if end of the list
  739.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  740.          ADD     BX, 4            ;; Point to next list entry
  741.          ADD     SI, AX           ;; Locate data in offscreen buffer
  742.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  743.          CheckBankUp 04E          ;; Change bank if necessary
  744.          CopyBytes                ;; Copy the bytes
  745.          TEST    DI, DI           ;; Did range end on a bank boundary?
  746.          JZ      NewBankUp05E     ;; Skip if range ended on a bank boundary
  747. NewBankUp05ERet:
  748.          JMP     LoopCopy2        ;; Loop once for each range
  749.  
  750.          ALIGN   16
  751. DirtyDone2:
  752.          TEST    DI, DI           ;; Did we cross the bank boundary?
  753.          JGE     DirtyCrossed2    ;; Skip if we already crossed
  754.          DoBankUp                 ;; Cross the bank boundary
  755. DirtyCrossed2:
  756.          POP     ES               ;; Restore registers
  757.          POP     DS               ;;
  758.          POP     DI               ;;
  759.          POP     SI               ;;
  760.          POP     DX               ;;
  761.          POP     BX               ;;
  762.          POP     CX               ;;
  763.          RETN                     ;; Return to caller without epilog
  764.  
  765.          ALIGN   16
  766. NotDirty:
  767.          POP     CX               ;; Restore register
  768. else
  769.          PUSH    ECX              ;; Save register
  770.          MOV     ECX, DirtyListNext    ;; Offset of next dirty list entry
  771.          SUB     ECX, DirtyListFirst   ;; 4 * number of dirty list entries
  772.          JLE     NotDirty         ;; Skip if dirty list is empty
  773.  
  774.          PUSH    EBX              ;; Save registers
  775.          PUSH    EDX              ;;
  776.          PUSH    ESI              ;;
  777.          PUSH    EDI              ;;
  778.  
  779.          SHR     ECX, 2           ;; Number of dirty list entries
  780.          MOV     EBX, BadStart    ;; Onscreen offset of line start
  781.          MOV     EDX, EBX         ;; Copy onscreen offset of line start
  782.          NEG     EDX              ;; Offset in offscreen buffer of bank split?
  783.          ADD     EBX, WdBytes     ;; Onscreen offset of second line in pair
  784.          JC      FirstLineSplit   ;; Skip if first line in pair has bank split
  785.          MOV     EDX, EBX         ;; Copy onscreen offset of line start
  786.          NEG     EDX              ;; Offset in offscreen buffer of bank split
  787. FirstLineSplit:
  788.                                   ;; Merge adjacent ranges
  789.                                   ;; Split a range that splits a bank
  790. ;         MOV     DS, WORD PTR OffScreen +2  ;; Offscreen buffer is the source
  791.          MOV     ESI, DirtyListFirst        ;; Point to first dirty list entry
  792.          LEA     EDI, [ESI-4]               ;; Allow room for one split entry
  793. LoopNewRange:
  794.          MOV     AX, [ESI]        ;; Get start of range
  795.          MOV     [EDI], AX        ;; Save start of range
  796. LoopSameRange:
  797.          MOV     EBX, [ESI+2]     ;; Get end of range
  798.          ADD     ESI, 4           ;; Point to next source slot
  799.          DEC     ECX              ;; One fewer source slot
  800.          JLE     RangeDone        ;; Skip if no more source slots
  801.          CMP     EBX, [ESI]       ;; Is the next range adjacent to this?
  802.          JB      RangeDone        ;; Skip if ranges are not adjacent
  803.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  804. RangeDone:
  805.          CMP     AX, DX           ;; Might split occur in this range?
  806.          JGE     NoSplit          ;; Skip if split does not occur in this range
  807.          CMP     BX, DX           ;; Does split occur in this range?
  808.          JLE     NoSplit          ;; Skip if split does not occur in this range
  809.          MOV     [EDI+2], DX      ;;
  810.          ADD     EDI, 4           ;; Point to next target slot
  811.          MOV     [EDI], DX        ;; Start of new range
  812. NoSplit:
  813.          MOV     [EDI+2], BX      ;; Set right edge of range
  814.          ADD     EDI, 4           ;; Point to next target slot
  815.          TEST    CX, CX           ;; Any source slots left?
  816.          JG      LoopNewRange     ;; Loop while source slots remain
  817.                                   ;;
  818.          OR      WORD PTR [EDI], -1;; Terminate the list
  819.                                   ;; Make offsets relative to prior entries
  820.                                   ;; Convert ending offsets to byte sizes
  821.          LEA     ECX, [EDI+4]     ;; Compute number of list entries
  822.          SUB     ECX, DirtyListFirst   ;; 4 * number of WORDs in the list
  823.          LEA     EBX, [EDI-2]     ;; Point to last WORD in list
  824.          SHR     ECX, 1           ;; 2 * number of WORDs in list
  825.          DEC     ECX              ;; Don't process first WORD
  826. LoopDiff:
  827.          MOV     AX, [EBX-2]      ;; Get previous WORD in list
  828.          SUB     [EBX], AX        ;; Compute difference from previous WORD
  829.          SUB     EBX, 2           ;; Back up one WORD
  830.          DEC     CX               ;; One fewer WORD
  831.          JG      LoopDiff         ;; Loop once for each WORD in list
  832.  
  833.                                   ;; Copy the first line of the pair
  834. ;         MOV     ES, SelVRAM      ;; VRAM is the target
  835.          MOV     EDI, BadStart    ;;
  836.          MOV     ESI, DWORD PTR OffScreen   ;; Offscreen buffer is the source
  837. LoopCopy1:
  838.          MOV     AX, [EBX]        ;; Get offset of range start
  839.          TEST    AX, AX           ;; End of the list?
  840.          JL      DirtyDone1       ;; Skip if end of the list
  841.          MOV     CX, [EBX+2]      ;; Get number of bytes in the range
  842.          ADD     EBX, 4           ;; Point to next list entry
  843.          ADD     ESI, EAX         ;; Locate data in offscreen buffer
  844.          ADD     EDI, EAX         ;; Position pointer in onscreen buffer
  845.          CheckBankUp 01E          ;; Change bank if necessary
  846.          CopyBytes                ;; Copy the bytes
  847.          TEST    EDI, EDI         ;; Did range end on a bank boundary?
  848.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  849. NewBankUp02ERet:
  850.          JMP     LoopCopy1        ;; Loop once for each range
  851.  
  852.          ALIGN   16
  853.                                   ;; Copy the second line of the pair
  854. DirtyDone1:
  855.          MOV     EAX, BadStart    ;; Onscreen offset of first line in pair
  856.          ADD     EAX, WdBytes     ;; Onscreen offset of second line in pair
  857.          SUB     EAX, EDI         ;; Offset from current target pointer
  858.          ADD     EDI, EAX         ;; Add the offset back in
  859.          CheckBankUp 03E          ;; Change bank if necessary
  860.          MOV     ESI, DWORD PTR OffScreen   ;; Offscreen buffer is the source
  861.          MOV     EBX, DirtyListFirst        ;; Point to first dirty list entry
  862.          SUB     EBX, 4                     ;; Post-processed first entry
  863. LoopCopy2:
  864.          MOV     AX, [EBX]        ;; Get offset of range start
  865.          MOVSX   EAX, AX
  866.          TEST    EAX, EAX         ;; End of the list?
  867.          JL      DirtyDone2       ;; Skip if end of the list
  868.          MOV     CX, [EBX+2]      ;; Get number of bytes in the range
  869.          ADD     EBX, 4           ;; Point to next list entry
  870.          ADD     ESI, EAX         ;; Locate data in offscreen buffer
  871.          ADD     EDI, EAX         ;; Position pointer in onscreen buffer
  872.          CheckBankUp 04E          ;; Change bank if necessary
  873.          CopyBytes                ;; Copy the bytes
  874.          TEST    EDI, EDI         ;; Did range end on a bank boundary?
  875.          JZ      NewBankUp05E     ;; Skip if range ended on a bank boundary
  876. NewBankUp05ERet:
  877.          JMP     LoopCopy2        ;; Loop once for each range
  878.  
  879.          ALIGN   16
  880. DirtyDone2:
  881.          TEST    EDI, EDI         ;; Did we cross the bank boundary?
  882.          JGE     DirtyCrossed2    ;; Skip if we already crossed
  883.          DoBankUp                 ;; Cross the bank boundary
  884. DirtyCrossed2:
  885.          POP     EDI              ;; Restore registers
  886.          POP     ESI              ;;
  887.          POP     EDX              ;;
  888.          POP     EBX              ;;
  889.          POP     ECX              ;;
  890.          RETN                     ;; Return to caller without epilog
  891.  
  892.          ALIGN   16
  893. NotDirty:
  894.          POP     ECX              ;; Restore register
  895. endif
  896.          DoBankUp                 ;; Go to next bank
  897.          RETN                     ;; Return to caller without epilog
  898.          ENDM                     ;; of GoOnScreen2x
  899.  
  900. COMMENT @
  901. Macro used to move pixels from onscreen buffer to offscreen buffer.
  902. @
  903. ifndef _QTW32_
  904. GoOffScreenX MACRO
  905.          LOCAL   NewBank
  906.          LOCAL   AllFits
  907.          LOCAL   ExitPath
  908.          ALIGN   16
  909. GoOffScreen:
  910.          PUSH    DS               ;; Save register
  911.          PUSH    CX               ;; Save register
  912.          PUSH    DX               ;; Save register
  913.          PUSH    SI               ;; Save register
  914.          PUSH    DI               ;; Save register
  915.          MOV     CX, AX           ;; Get byte count
  916.          MOV     DS, SelVRAM      ;; Point to onscreen buffer
  917.          MOV     SI, DI           ;; Copy target offset
  918.          SUB     SI, WORD PTR OffScreen ;; Compute offset from start of buffer
  919.          ADD     SI, BadStart     ;; Index into onscreen buffer
  920.          JC      NewBank          ;; Skip if we crossed a bank boundary
  921.          ADD     SI, CX           ;; Check ending source offset
  922.          JNC     AllFits          ;; Skip if no bank change required
  923.          JZ      AllFits          ;; Skip if no bank change required
  924.          PUSH    SI               ;; Save byte count for new bank
  925.          SUB     SI, CX           ;; Restore source pointer
  926.          MOV     CX, SI           ;; Compute byte count for old bank
  927.          NEG     CX               ;;
  928.          CopyBytes                ;; Copy the bytes
  929.          POP     CX               ;; Get byte count for new bank
  930. NewBank:
  931.          DoBankUp                 ;; Go to new bank
  932.          CopyBytes                ;; Copy the bytes
  933.          DoBankDn                 ;; Back to old bank
  934. ExitPath:
  935.          POP     DI               ;; Restore register
  936.          POP     SI               ;; Restore register
  937.          POP     DX               ;; Restore register
  938.          POP     CX               ;; Restore register
  939.          POP     DS               ;; Restore register
  940.          RETN                     ;; Return to caller without epilog
  941.  
  942.          ALIGN   16
  943. AllFits:                          ;; Source does not cross a bank boundary
  944.          SUB     SI, CX           ;; Restore source pointer
  945.          CopyBytes                ;; Copy the bytes
  946.          JMP     ExitPath         ;; Go to common exit
  947.          ENDM
  948. else
  949. GoOffScreenX MACRO
  950.          LOCAL   NewBank
  951.          LOCAL   AllFits
  952.          LOCAL   ExitPath
  953.          ALIGN   16
  954. GoOffScreen:
  955.          PUSH    ECX              ;; Save register
  956.          PUSH    EDX              ;; Save register
  957.          PUSH    ESI              ;; Save register
  958.          PUSH    EDI              ;; Save register
  959.          MOV     ECX, EAX         ;; Get byte count
  960.          MOV     ESI, EDI         ;; Copy target offset
  961.          SUB     ESI, DWORD PTR OffScreen ;; Compute offset from start of buffer
  962.          ADD     ESI, BadStart    ;; Index into onscreen buffer
  963.          JC      NewBank          ;; Skip if we crossed a bank boundary
  964.          ADD     ESI, ECX         ;; Check ending source offset
  965.          JNC     AllFits          ;; Skip if no bank change required
  966.          JZ      AllFits          ;; Skip if no bank change required
  967.          PUSH    ESI              ;; Save byte count for new bank
  968.          SUB     ESI, ECX         ;; Restore source pointer
  969.          MOV     ECX, ESI         ;; Compute byte count for old bank
  970.          NEG     ECX              ;;
  971.          CopyBytes                ;; Copy the bytes
  972.          POP     ECX              ;; Get byte count for new bank
  973. NewBank:
  974.          DoBankUp                 ;; Go to new bank
  975.          CopyBytes                ;; Copy the bytes
  976.          DoBankDn                 ;; Back to old bank
  977. ExitPath:
  978.          POP     EDI              ;; Restore register
  979.          POP     ESI              ;; Restore register
  980.          POP     EDX              ;; Restore register
  981.          POP     ECX              ;; Restore register
  982.          RETN                     ;; Return to caller without epilog
  983.  
  984.          ALIGN   16
  985. AllFits:                          ;; Source does not cross a bank boundary
  986.          SUB     ESI, ECX         ;; Restore source pointer
  987.          CopyBytes                ;; Copy the bytes
  988.          JMP     ExitPath         ;; Go to common exit
  989.          ENDM
  990. endif
  991.  
  992. COMMENT @
  993. Macro to compute number of source bytes processed, used for BMP banding
  994. @
  995. BytesUsed MACRO
  996. ifndef _QTW32_
  997.          XOR     EAX, EAX           ;; Zero out high order word
  998.          MOV     AX, WORD PTR Inbuf ;; Get offset of source pointer
  999.          SUB     ESI, EAX           ;; Compute number of source bytes processed
  1000.          SHLD    EDX, ESI, 16       ;; Put high order word in DX
  1001.          MOV     AX, SI             ;; Put low order word in AX
  1002. else
  1003.         MOV     EAX, DWORD PTR Inbuf ;; Where did we start
  1004.         SUB     ESI, EAX             ;; How far did we get
  1005.         MOV     EAX, ESI             ;; Return it
  1006. endif
  1007.          ENDM
  1008.  
  1009. COMMENT @
  1010. Macros for moving pixels to and from overscan area
  1011.  
  1012. Note that the 2X versions of the macros should not be used with 8-bit
  1013. targets since dithering will produce different target values for the
  1014. same source.
  1015.  
  1016. We generate error messages only in the SavePixels macro, figuring they
  1017. would be superfluous in RestPixels.
  1018. @
  1019. SaveOneRow MACRO
  1020. j        =       0
  1021.          REPEAT  Limit
  1022. ifndef _QTW32_
  1023.          MOV     EAX, ES:[DI+j]   ;; Get 4 bytes
  1024. else
  1025.          MOV     EAX, [EDI+j]   ;; Get 4 bytes
  1026. endif
  1027.          MOV     OverScan[i], EAX ;; Save the bytes
  1028. i        =       i + 4
  1029. j        =       j + 4
  1030.          ENDM                     ;; of REPEAT Limit
  1031.          ENDM
  1032.  
  1033. RestOneRow MACRO Width:REQ
  1034. j        =       0
  1035.          WHILE   j  LT  Width - Width MOD 4
  1036.          MOV     EAX, OverScan[i] ;; Get 4 bytes
  1037. ifndef _QTW32_
  1038.          MOV     ES:[DI+j], EAX   ;; Restore the bytes
  1039. else
  1040.          MOV     [EDI+j], EAX   ;; Restore the bytes
  1041. endif
  1042. i        =       i + 4
  1043. j        =       j + 4
  1044.          ENDM                     ;; of WHILE  j  LT  Width - Width MOD 4
  1045.          IF      j  LT  Width - Width MOD 2
  1046.          MOV     AX, WORD PTR OverScan[i]   ;; Get 2 bytes
  1047. ifndef _QTW32_
  1048.          MOV     ES:[DI+j], AX              ;; Restore the bytes
  1049. else
  1050.          MOV     [EDI+j], AX              ;; Restore the bytes
  1051. endif
  1052. i        =       i + 2
  1053. j        =       j + 2
  1054.          ENDIF                    ;; of IF      j  LT  Width - Width MOD 2
  1055.          IF      j  LT  Width
  1056.          MOV     AL, BYTE PTR OverScan[i]   ;; Get 1 byte
  1057. ifndef _QTW32_
  1058.          MOV     ES:[DI+j], AL              ;; Restore the byte
  1059. else
  1060.          MOV     [EDI+j], AL              ;; Restore the byte
  1061. endif
  1062. i        =       i + 1
  1063. j        =       j + 1
  1064.          ENDIF                    ;; of IF      j  LT  Width
  1065. i        =       (i + 3) AND 0FFFCH    ;; Round up to multiple of 4
  1066.          ENDM                     ;; of RestOneRow MACRO
  1067.  
  1068. SavePixels MACRO Width:REQ, Height:REQ, Increment:REQ
  1069. i        =       0
  1070. Limit    =       (((Width) + 3) AND 0FFFCH) / 4
  1071. ReqdSize TEXTEQU %(Limit * Height)
  1072.          IFNDEF  OverScan
  1073. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  1074.          EXITM
  1075.          ENDIF
  1076.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  1077. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  1078.          EXITM
  1079.          ENDIF
  1080.          REPEAT  Height
  1081. ifndef _QTW32_
  1082.          ADD     DI, Increment
  1083. else
  1084.          ADD     EDI, Increment
  1085. endif
  1086.          SaveOneRow
  1087.          ENDM                     ;; of REPEAT Height
  1088.          ENDM                     ;; of MACRO
  1089.  
  1090. RestPixels MACRO Width:REQ, Height:REQ, Increment:REQ
  1091. i        =       0
  1092. Limit    =       (((Width) + 3) AND 0FFFCH) / 4
  1093. ReqdSize TEXTEQU %(Limit * Height)
  1094.          IFNDEF  OverScan         ;; If variable not defined
  1095.          EXITM                    ;; Avoid further error messages
  1096.          ENDIF
  1097.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  1098.          EXITM
  1099.          ENDIF
  1100.          REPEAT  Height
  1101. ifndef _QTW32_
  1102.          ADD     DI, Increment
  1103. else
  1104.          ADD     EDI, Increment
  1105. endif
  1106.          RestOneRow (Width)
  1107.          ENDM                     ;; of REPEAT Height
  1108.          ENDM                     ;; of MACRO
  1109.  
  1110. SavePix2X MACRO Width:REQ, Height:REQ, Increment:REQ
  1111. i        =       0
  1112. Limit    =       (((Width) * 2 + 3) AND 0FFFCH) / 4
  1113. ReqdSize TEXTEQU %(Limit * Height)
  1114.          IFNDEF  OverScan
  1115. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  1116.          EXITM
  1117.          ENDIF
  1118.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  1119. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  1120.          EXITM
  1121.          ENDIF
  1122.          REPEAT  Height
  1123. ifndef _QTW32_
  1124.          ADD     DI, Increment
  1125.          ADD     DI, Increment
  1126. else
  1127.          ADD     EDI, Increment
  1128.          ADD     EDI, Increment
  1129. endif
  1130.          SaveOneRow
  1131.          ENDM                     ;; of REPEAT Height
  1132.          ENDM                     ;; of MACRO
  1133.  
  1134. RestPix2X MACRO Width:REQ, Height:REQ, Increment:REQ
  1135. i        =       0
  1136. Limit    =       (((Width) * 2 + 3) AND 0FFFCH) / 4
  1137. ReqdSize TEXTEQU %(Limit * Height)
  1138.          IFNDEF  OverScan         ;; If variable not defined
  1139.          EXITM                    ;; Avoid further error messages
  1140.          ENDIF
  1141.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  1142.          EXITM
  1143.          ENDIF
  1144. ifndef _QTW32_
  1145.          PUSH    BX               ;; Save register
  1146.          MOV     BX, Increment    ;; Initialize index register
  1147. j        =       0
  1148.          REPEAT  Limit            ;; Restore row #2 from row #1
  1149.          MOV     EAX, ES:[DI+j]   ;; Get 4 uncorrupted bytes
  1150.          MOV     ES:[DI+BX+j], EAX;; Restore the bytes
  1151. j        =       j + 4
  1152.          ENDM                     ;; of REPEAT Limit
  1153.          REPEAT  Height - 1
  1154.          ADD     DI, BX
  1155.          ADD     DI, BX
  1156. j        =       0
  1157.          REPEAT  Limit
  1158.          MOV     EAX, OverScan[i] ;; Get 4 saved bytes
  1159.          MOV     ES:[DI+j], EAX   ;; Restore the bytes to row #1
  1160.          MOV     ES:[DI+BX+j], EAX;; Restore the bytes to row #2
  1161. i        =       i + 4
  1162. j        =       j + 4
  1163.          ENDM                     ;; of REPEAT Limit
  1164.          ENDM                     ;; of REPEAT Height
  1165.          ADD     DI, BX           ;; Point to last row
  1166.          ADD     DI, BX
  1167.          RestOneRow ((Width) * 2)
  1168.          POP     BX               ;; Restore register
  1169. else
  1170.          PUSH    EBX              ;; Save register
  1171.          MOV     EBX, Increment   ;; Initialize index register
  1172. j        =       0
  1173.          REPEAT  Limit            ;; Restore row #2 from row #1
  1174.          MOV     EAX, [EDI+j]     ;; Get 4 uncorrupted bytes
  1175.          MOV     [EDI+EBX+j], EAX ;; Restore the bytes
  1176. j        =       j + 4
  1177.          ENDM                     ;; of REPEAT Limit
  1178.          REPEAT  Height - 1
  1179.          ADD     EDI, EBX
  1180.          ADD     EDI, EBX
  1181. j        =       0
  1182.          REPEAT  Limit
  1183.          MOV     EAX, OverScan[i] ;; Get 4 saved bytes
  1184.          MOV     [EDI+j], EAX   ;; Restore the bytes to row #1
  1185.          MOV     [EDI+EBX+j], EAX;; Restore the bytes to row #2
  1186. i        =       i + 4
  1187. j        =       j + 4
  1188.          ENDM                     ;; of REPEAT Limit
  1189.          ENDM                     ;; of REPEAT Height
  1190.          ADD     EDI, EBX         ;; Point to last row
  1191.          ADD     EDI, EBX
  1192.          RestOneRow ((Width) * 2)
  1193.          POP     EBX              ;; Restore register
  1194. endif
  1195.          ENDM                     ;; of MACRO
  1196.